#include <QUdpSocket>
#include <QDialog>
#include <QLabel>
#include <QPushButton>
#include <QLineEdit>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QIntValidator>
#include <QRegExp>
#include <QTimerEvent>
#include <QFile>
#include <QDateTime>
#include <QCloseEvent>

#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <termios.h>
#include <iostream>

#include "linkquality.h"

using namespace std;

const char remoteAddr[100] = "192.168.1.104";
const int CMD_PORT = 11112;
const int UDP_PORT = 11111;
const char COM[20] = "/dev/ttyS0";

LinkQuality::LinkQuality(QWidget *parent)
    :QDialog(parent)
{
    loadGUI();

    //lock log file for open
    fileLock.lockForWrite();

    //open file
    file = new QFile("linkQualityLog.txt");
    file->open(QIODevice::Append);

    //create file stream
    logFile = new QTextStream(file);

    //unlock
    fileLock.unlock();

    //start UDP server and local GPS update threads
    startOrStopThreads();
}

void LinkQuality::loadGUI()
{
    setFixedSize(320, 240);

    //create ip address label and line edit
    ipLabel = new QLabel(tr("&IP:"));
    ipEdit = new QLineEdit("192.168.1.100");
    QRegExp regExp("[1-9][0-9]{0,2}[.][0-9]{0,3}[.][0-9]{0,3}[.][0-9]{0,3}");
    ipEdit->setValidator(new QRegExpValidator(regExp, this));
    ipLabel->setBuddy(ipEdit);

    //create port label and line edit
    portLabel = new QLabel(tr("&Port:"));
    portEdit = new QLineEdit("11111");
    portEdit->setValidator(new QIntValidator);
    portLabel->setBuddy(portEdit);

    //create packet length label and line edit
    packetLengthLabel = new QLabel(tr("Length"));
    packetLengthEdit = new QLineEdit("10");

    //create send count label and line edit
    countLabel = new QLabel(tr("Count"));
    countEdit = new QLineEdit("20");

    //create GPS time label and line edit
    localeTimeLabel = new QLabel(QString("Time:").leftJustified(15));
    localeTimeEdit = new QLineEdit;

    //create GPS longitude label and line edit
    localeLongitudeLabel = new QLabel(QString("Longtitude:").leftJustified(15));
    localeLongitudeEdit = new QLineEdit;

    //create GPS latitude label and line edit
    localeLatitudeLabel = new QLabel(QString("Latitude:").leftJustified(15));
    localeLatitudeEdit = new QLineEdit;

    //create send button and close button
    startButton = new QPushButton(tr("Send"));
    closeButton = new QPushButton(tr("Close"));

    //create ip address and port layout
    QHBoxLayout *ipPortLayout = new QHBoxLayout;
    ipPortLayout->addWidget(ipLabel);
    ipPortLayout->addWidget(ipEdit);
    ipPortLayout->addWidget(portLabel);
    ipPortLayout->addWidget(portEdit);

    //create packet length layout
    QHBoxLayout *packetLengthLayout = new QHBoxLayout;
    packetLengthLayout->addWidget(packetLengthLabel);
    packetLengthLayout->addWidget(packetLengthEdit);

    //create send count layout
    QHBoxLayout *countLayout = new QHBoxLayout;
    countLayout->addWidget(countLabel);
    countLayout->addWidget(countEdit);

    //create GPS time layout
    QHBoxLayout *localeTimeLayout = new QHBoxLayout;
    localeTimeLayout->addWidget(localeTimeLabel);
    localeTimeLayout->addWidget(localeTimeEdit);

    //create GPS longitude layout
    QHBoxLayout *localeLongitudeLayout = new QHBoxLayout;
    localeLongitudeLayout->addWidget(localeLongitudeLabel);
    localeLongitudeLayout->addWidget(localeLongitudeEdit);

    //create GPS latitude layout
    QHBoxLayout *localeLatitudeLayout = new QHBoxLayout;
    localeLatitudeLayout->addWidget(localeLatitudeLabel);
    localeLatitudeLayout->addWidget(localeLatitudeEdit);

    //create button layout
    QHBoxLayout *buttomLayout = new QHBoxLayout;
    buttomLayout->addWidget(startButton);
    buttomLayout->addWidget(closeButton);

    //create main layout
    QVBoxLayout *mainLayout = new QVBoxLayout;

    //add GPS layout to main layout
    mainLayout->addLayout(localeTimeLayout);
    mainLayout->addLayout(localeLongitudeLayout);
    mainLayout->addLayout(localeLatitudeLayout);

    //add packet length and send count layout to main layout
    mainLayout->addLayout(packetLengthLayout);
    mainLayout->addLayout(countLayout);

    mainLayout->addLayout(ipPortLayout);
    mainLayout->addLayout(buttomLayout);
    setLayout(mainLayout);

    connect(startButton, SIGNAL(clicked()), this, SLOT(send()));
    connect(closeButton, SIGNAL(clicked()), this, SLOT(close()));
}

void LinkQuality::localFinish()
{
    cout<<"localFinish"<<endl;
}

void LinkQuality::remoteFinish()
{
    cout<<"remoteFinish"<<endl;
}

void LinkQuality::startOrStopThreads()
{
    cout<<"start threads"<<endl;
    localThread = new updateGPS;
    remoteThread = new updateRemoteGPS;

    connect(localThread, SIGNAL(updateData(QString,QString,QString)), this, SLOT(updateGPSData(QString,QString,QString)));
    connect(localThread, SIGNAL(finished()), this, SLOT(localFinish()));
    connect(remoteThread, SIGNAL(updateRemoteData(QByteArray)), this, SLOT(updateRemoteGPSData(QByteArray)));

    localThread->start();
    remoteThread->start();
}

void LinkQuality::send()
{

    cout<<"send"<<endl;
    int size = packetLengthEdit->text().toInt();
    int count = countEdit->text().toInt();

    //write message to log file
    gpsLock.lockForRead();
    fileLock.lockForWrite();
    *logFile<<"*********************************************"<<endl;
    *logFile<<"local:"<<endl<<"  "<<this->time.toAscii()<<"  "<<this->longitude.toAscii()<<"  "<<latitude.toAscii()
           <<"   "<<count<<"   "<<size<<endl;
    fileLock.unlock();
    gpsLock.unlock();

    QByteArray datagram;
    QDataStream out(&datagram, QIODevice::WriteOnly);
    out<<qint16(1);
    datagram.resize(size);
    cout<<datagram.size()<<endl;
    QUdpSocket sendSocket;
    for(int i = 0; i < count; i++)
    {
        sendSocket.writeDatagram(datagram, datagram.size(), QHostAddress(remoteAddr), CMD_PORT);
        //sleep(1);
    }
}

void LinkQuality::updateGPSData(QString time, QString longtitude, QString latitude)
{
    localeTimeEdit->setText(time);
    localeLongitudeEdit->setText(longtitude);
    localeLatitudeEdit->setText(latitude);

    gpsLock.lockForWrite();
    this->time = time;
    this->longitude = longtitude;
    this->latitude = latitude;
    gpsLock.unlock();
}

void LinkQuality::updateRemoteGPSData(QByteArray data)
{
    cout<<"updateRemoteGPSData"<<endl;
    qint16 type;
    QDataStream in(&data, QIODevice::ReadOnly);
    QString gps;
    in>>type;
    if(type == 2)
    {
        in>>gps;
        QStringList datas = gps.split(",");
        fileLock.lockForWrite();
        *logFile<<"remote:"<<endl<<"  "<<datas.at(1).toAscii()<<"  "<<datas.at(3).toAscii()<<"  "<<datas.at(5).toAscii()<<endl;
        fileLock.unlock();
    }
    else if(type == 3)
    {
        cout<<data.size()<<endl;
        if(data.size() == packetLengthEdit->text().toInt())
        {
            fileLock.lockForWrite();
            *logFile<<data.size()<<endl;
            fileLock.unlock();
        }
    }

}

void LinkQuality::closeEvent(QCloseEvent *event)
{
    if(threadsIsRunning)
    {
        localThread->stop();
        remoteThread->quit();
        localThread->wait();
        remoteThread->wait();
    }
    event->accept();
}

void LinkQuality::timerEvent(QTimerEvent *event)
{
    int timerID = event->timerId();
    if(timerID == localTimer)
    {
        localeTimeEdit->clear();
        localeLongitudeEdit->clear();
        localeLatitudeEdit->clear();
    }
    else if(timerID == remoteTimer)
    {
        remoteTimeEdit->clear();
        remoteLongitudeEdit->clear();
        remoteLatitudeEdit->clear();
    }
}

void updateGPS::stop()
{
    stopped = true;
}

int updateGPS::openPort()
{
    int fd;
    fd = ::open(COM, O_RDWR | O_NOCTTY | O_NDELAY);
    cout<<"open COM "<<COM<<endl;
    if(fd == -1)
    {
        perror("open_port: Unable to open COM - ");
    }
    else
    {
        fcntl(fd, F_SETFL, 0);
    }

    return fd;
}

void updateGPS::run()
{
    struct termios options;
    int fd;

    fd = this->openPort();
    /*
     *Get the current options for the port...
     */
    tcgetattr(fd, &options);

    /*
     *Set the baud rates to 19200...
     */
    cfsetispeed(&options, B9600);
    cfsetospeed(&options, B9600);
    /*
     *Set Parity check
     */
    options.c_cflag &= ~PARENB;
    options.c_cflag &= ~CSTOPB;
    options.c_cflag &= ~CSIZE;
    options.c_cflag |= CS8;
    /*
     *Set hardware flow control
     */
    //options.c_cflag &= ~CNEW_RTSCTS;

    /*
     *Enable the receiver and set local mode ...
     */
    options.c_cflag |= (CLOCAL | CREAD);
    /*
     *Set the new options for the port...
     */
    tcsetattr(fd, TCSANOW, &options);

    fcntl(fd, F_SETFL, 0);
    char buff[1024];

    QString str;
    QTextStream stream(&str);
    QString line;
    line.clear();

    while(stopped == false)
    {
        int readn = 0;
        memset(buff, 0, sizeof(buff));
        readn = ::read(fd, buff, sizeof(buff));
        int i = 0;
        while(buff[i] == 0) i++;
        stream<<QByteArray(buff + i, readn);
        while((line = stream.readLine()) != NULL)
        {
            char *charLine = line.toAscii().data();

            if(strlen(charLine) <= 4 || charLine[0] != '$' || charLine[line.length() - 3] != '*')
            {
                stream<<line;
                //<<"false"<<endl;
                break;
            }
            //cout<<"line: "<<line.toAscii().data()<<endl;
            if(line.indexOf(QString("GPRMC")) != -1)
            {
                QStringList datas = line.split(",");
                emit updateData(datas.at(1), datas.at(3), datas.at(5));
                cout<<"locale: "<<line.toAscii().data()<<endl;
            }
        }
    }//end while(1)
    cout<<"stopped"<<endl;
    ::close(fd);
}

updateRemoteGPS::updateRemoteGPS()
{
    receiveSocket = new QUdpSocket;
    receiveSocket->bind(UDP_PORT);

    connect(receiveSocket, SIGNAL(readyRead()), this, SLOT(processPendingDatagrams()));
}

updateRemoteGPS::~updateRemoteGPS()
{
    receiveSocket->close();
}

void updateRemoteGPS::processPendingDatagrams()
{
    //cout<<"processPendingDatagrams"<<endl;
    QByteArray datagram;

    datagram.resize(receiveSocket->pendingDatagramSize());
    receiveSocket->readDatagram(datagram.data(), datagram.size());

    cout<<"datagram size: "<<datagram.size()<<endl;

    QDataStream in(&datagram, QIODevice::ReadOnly);

    quint16 type;
    in>>type;
    if(type == 2 || type == 3)
    {
        emit updateRemoteData(datagram);
    }
}
